//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#pragma once

#ifdef RIL_USE_CPIN2_WORKAROUND
#include <wincrypt.h>
#endif

//
// Character mapping structure used for binary search
//
struct CHARMAP {
    char ch;
    WCHAR wch;
};

//
// Encoding types
//
enum ENCODING_TYPE {
    ENCODING_GSMDEFAULT = 0,// GSM Default alphabet
    ENCODING_GSMDEFAULT_HEX, // GSM Default alphabet, HEX encoded
    // Others to be added later
    ENCODING_UCS2 = 0x8000,
    ENCODING_ASCII
};
#define ENCODING_TECHARSET ENCODING_GSMDEFAULT


// Macro for retail trace output of RIL driver
// Sample usage: RILRetailTrace((TEXT("RilDrv: Debug-Info %x"), 66));
#if defined(WANT_RETAIL_RILDRV_OUTPUT) && defined(WANT_FILE_RILDRV_OUTPUT)
#ifdef WANT_RILDRV_OUTPUT_TIMESTAMP
#define RILRetailTrace(printf_exp) NKDbgWithTimeStamp printf_exp; DbgWrite printf_exp
#else
#define RILRetailTrace(printf_exp) NKDbgPrintfW printf_exp; DbgWrite printf_exp
#endif
#elif defined(WANT_RETAIL_RILDRV_OUTPUT)
#ifdef WANT_RILDRV_OUTPUT_TIMESTAMP
#define RILRetailTrace(printf_exp) NKDbgWithTimeStamp printf_exp
#else
#define RILRetailTrace(printf_exp) NKDbgPrintfW printf_exp
#endif
#elif defined(WANT_FILE_RILDRV_OUTPUT)
#define RILRetailTrace(printf_exp) DbgWrite printf_exp
#else
#define RILRetailTrace(printf_exp)
#endif


// Macro to round value to DWORD boundary (used to meet ARM memory alignment requirements)
#define ROUNDTODWORD(p)     (DWORD)(4 * (((DWORD) p + 3) / 4))


//
// Function declarations
//
BOOL FindString(DWORD dwStartPos, char *pszString1, char *pszString2, UINT uiLen, PDWORD pdwEndPos);
void TraceCRLFString(LPCSTR szIn, LPCTSTR szText=NULL, LPCTSTR szIn2=NULL);
void ExtRILRetailTrace(LPCTSTR szText, LPCTSTR szIn1, LPCSTR szIn2);
BOOL UnicodeCharToGSM(const WCHAR wch, char *pchRet);
BOOL GSMCharToUnicode(const char ch, WCHAR *pwchRet);
char SemiByteToChar(const BYTE bByte, const BOOL fHigh);
BYTE SemiByteCharsToByte(const char chHigh, const char chLow);
BOOL GSMHexToGSM(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed);
BOOL GSMToGSMHex(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed);
BOOL ConvertFromUnicode(const ENCODING_TYPE enc, const LPCWSTR wsIn, const UINT cchIn, const LPSTR sOut, const UINT cbOut,
                        UINT& rcbUsed);
BOOL ConvertToUnicode(const ENCODING_TYPE enc, const LPCSTR sIn, const UINT cbIn, const LPWSTR wsOut, const UINT cchOut,
                      UINT& rcchUsed);
BOOL ConvertTECharSetToUnicode(const LPCSTR sIn, const UINT cchIn,
                               const LPWSTR wsOut, const UINT cchOut,
                               UINT * pcchUsed);
BOOL ConvertUnicodeToTECharSet(const LPWSTR wsIn, const UINT cchIn,
                               const LPSTR sOut, const UINT cchOut,
                               UINT * pcchUsed);
HRESULT RILAddressToString(const RILADDRESS& rraAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbTypeOfAddress);
BOOL StringToRILAddress(const LPCSTR szAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress);
HRESULT RILSubAddressToString(const RILSUBADDRESS& rrsaSubAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbType);
BOOL StringToRILSubAddress(const LPCSTR szSubAddress, const BYTE bType, RILSUBADDRESS& rrsaSubAddress);
HRESULT DetermineSimResponseError(DWORD dwSW1, DWORD dwSW2);
BOOL IsElementarySimFile(DWORD dwFileID);
BOOL ComposeCmdWithByteArray(const LPCSTR szPrefix, const BYTE* const pbBytes, const UINT cbBytes,
                             const LPCSTR szPostfix, LPSTR& rszCmd);
char * BeginLineSpecificCommand(char *szCmdDst, UINT cchSize, DWORD dwAddressId);
BOOL AllocateOrReallocateStorage(BYTE** const prgrData, const size_t stSize, const UINT nUsed, UINT* const pnAllocated,
                                 const UINT nGrowSize);
void UpdateStatStore();
void UpdateSIMState(DWORD dwSimState);

BOOL UCS2StringToRILAddress(const LPWSTR wszAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress);
HRESULT RILAddressToUCS2String(const RILADDRESS& rraAddress, const LPWSTR wszOut, const UINT cbOut, BYTE& rbTypeOfAddress);
BOOL UCS2ToGSMHex(const LPWSTR pwszIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut);

#ifdef RIL_USE_CPIN2_WORKAROUND
void ClearPIN2Secure();
void SavePIN2Secure(LPCSTR szNewPIN);
BOOL FetchPIN2Secure(LPSTR szPinBuffer, DWORD cbBufferSIze);
#endif

//
// Class declarations
//

// Growable character buffer
class CBuffer
{
public:
            CBuffer();
            ~CBuffer();

    BOOL    Append(const LPCSTR szString, const UINT cbString);
    LPSTR   GetData() const     { return m_szData; };
    UINT    GetLength() const   { return m_cbLength; };
    LPSTR   GiveUpData();
    void    InheritData(CBuffer* pSrcBuffer);

protected:
    LPSTR   m_szData;
    UINT    m_cbLength;
    UINT    m_cbAlloc;
};


// Doubly-linked list element
class CListElem
{
public:
                CListElem() : m_pNext(NULL), m_pPrev(NULL) {};
                ~CListElem() {};

    CListElem*  GetNext() const         { return m_pNext; };
    CListElem*  GetPrev() const         { return m_pPrev; };

    void        SetNext(CListElem* p)   { m_pNext = p; };
    void        SetPrev(CListElem* p)   { m_pPrev = p; };

private:
    CListElem*  m_pNext;
    CListElem*  m_pPrev;
};


// Callback used when entering exclusive use mode
typedef void (*PFN_SHRDUSE_ACTION)(DWORD dwParam);


// Shared resource with readers/writers synchronization
class CSharedResource
{
public:
            CSharedResource();
            ~CSharedResource();

    BOOL    Init(const HANDLE hCancelEvent);
    BOOL    EnterSharedUse();
    BOOL    ExitSharedUse();
    BOOL    EnterExclusiveUse(PFN_SHRDUSE_ACTION pfnAction, const DWORD dwParam) const;
    BOOL    ExitExclusiveUse() const;

private:
    BOOL                m_fInited;
    CRITICAL_SECTION    m_cs;
    DWORD               m_dwSharedUsers;
    HANDLE              m_hSharedUseSph;
    HANDLE              m_hExclusiveUseSph;
    HANDLE              m_hCancelEvent;
};


// Function to be passed to CQueue::Enum().
//    This function should return TRUE for enumeration to stop.
typedef BOOL (*PFN_QUEUE_ENUM)(void* pItem, DWORD dwData);

// Function to be passed to CQueue::ConditionalGet().
//    This function should return TRUE for for the item to be removed from the queue.
typedef BOOL (*PFN_QUEUE_TEST)(void* pItem, DWORD dwData);

// Function to be used to destroy items in the queue
// when the queue is destroyed.
typedef void (*PFN_QUEUE_ITEMDTOR)(void *pItem);

template <class Type, UINT Size>
class CBoundedQueue
{
private:
    CEvent              m_eventSpace;
    CUniquelyNamedEvent m_eventItems;
    Type *              m_rgpItems[Size];
    UINT                m_nUsed;
    PFN_QUEUE_ITEMDTOR  m_pfnItemDtor;
    CRITICAL_SECTION    m_cs;

public:
            CBoundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor = NULL);
    virtual ~CBoundedQueue();

    BOOL    Ready() const { return m_eventSpace.Ready() && m_eventItems.Ready(); }
    BOOL    Init();
    BOOL    Put(Type* const pItem);
    BOOL    Get(Type** ppItem);
    BOOL    Peek(Type** ppItem);
    HRESULT ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type** rpItem);

    const
    TCHAR * GetItemsEventName() { return m_eventItems.GetName(); }
    HANDLE  GetItemsEvent() { return m_eventItems.GetHandle(); }
    HANDLE  GetSpaceEvent() { return m_eventSpace.GetHandle(); }
};

template <class Type, UINT Size>
CBoundedQueue<Type, Size>::CBoundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor)
:   m_nUsed(0),
    m_pfnItemDtor(pfnItemDtor)
{
    InitializeCriticalSection(&m_cs);

    for (UINT i = 0; i < Size; i++)
        m_rgpItems[i] = NULL;
}

template <class Type, UINT Size>
CBoundedQueue<Type, Size>::~CBoundedQueue()
{
    // In case someone is still waiting on
    // this queue, try to avoid conflicts...
    if (m_eventItems.Ready())
        m_eventItems.Reset();   // no items to dequeue
    if (m_eventSpace.Ready())
        m_eventSpace.Reset();   // no space for enqueue

    if (m_pfnItemDtor)
    {
        for (UINT i = 0; i < m_nUsed; i++)
        {
            m_pfnItemDtor(m_rgpItems[i]);
            m_rgpItems[i] = NULL;
        }
    }

    m_nUsed = 0;

    DeleteCriticalSection(&m_cs);
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Init()
{
    return
        m_eventSpace.Init(TRUE, TRUE) &&
        m_eventItems.Init(TRUE, FALSE, TEXT("CBoundedQueue_"));
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Put(Type * const pItem)
{
    ASSERT(Ready());

    SYNCBLOCK(m_cs);

    if (m_nUsed < Size)
    {
        // Put the element in our queue.
        m_rgpItems[m_nUsed++] = pItem;

        // If this filled the queue buffer,
        // then reset the space avail event.
        if (m_nUsed == Size)
            m_eventSpace.Reset();

        // There's something new in the queue,
        // so signal waiting threads.
        m_eventItems.Set();

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Get(Type ** ppItem)
{
    return ConditionalGet(NULL, 0, ppItem);
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Peek(Type ** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        *ppItem = m_rgpItems[0];

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type, UINT Size>
HRESULT CBoundedQueue<Type, Size>::ConditionalGet(const PFN_QUEUE_TEST pfnTest,
                                                  const DWORD dwData,
                                                  Type** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return S_FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        Type * pItem = m_rgpItems[0];

        // If there is a condition, make sure
        // it is satisfied.
        if (pfnTest && !pfnTest(pItem, dwData))
            return E_FAIL;

        // One less in queue now...
        m_nUsed--;

        if (m_nUsed)
        {
            // Slide remaining items over to the beginning of the buffer.
            memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);

            // If we just made space, then set this event.
            if (Size - 1 == m_nUsed)
                m_eventSpace.Set();
        }
        else
        {
            // The last item was copied from the queue,
            // so reset this event.
            m_eventItems.Reset();
        }

        // NULL out the space that was freed up.
        m_rgpItems[m_nUsed] = NULL;

        // Set the output parameter.
        *ppItem = pItem;

        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

template <class Type>
class CUnboundedQueue
{
private:
    CUniquelyNamedEvent m_eventItems;
    Type **             m_rgpItems;
    DWORD               m_dwInitialSize;
    DWORD               m_dwCurrentSize;
    UINT                m_nUsed;
    PFN_QUEUE_ITEMDTOR  m_pfnItemDtor;
    CRITICAL_SECTION    m_cs;

protected:
    BOOL    GrowQueue();

public:
            CUnboundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor = NULL);
    virtual ~CUnboundedQueue();

    BOOL    Ready() const { return m_rgpItems != NULL && m_eventItems.Ready(); }
    BOOL    Init(DWORD dwInitialSize);
    BOOL    Put(Type* const pItem);
    BOOL    Get(Type** ppItem);
    BOOL    Peek(Type** ppItem);
    HRESULT ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type** rpItem);

    const
    TCHAR * GetItemsEventName() { return m_eventItems.GetName(); }
    HANDLE  GetItemsEvent() { return m_eventItems.GetHandle(); }

    UINT    GetSize() { return m_nUsed; }
    DWORD   GetCurrentBufferSize() { return m_dwCurrentSize; }
};

template <class Type>
CUnboundedQueue<Type>::CUnboundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor)
:   m_nUsed(0),
    m_dwInitialSize(0),
    m_dwCurrentSize(0),
    m_rgpItems(NULL),
    m_pfnItemDtor(pfnItemDtor)
{
    InitializeCriticalSection(&m_cs);
}

template <class Type>
CUnboundedQueue<Type>::~CUnboundedQueue()
{
    // In case someone is still waiting on
    // this queue, try to avoid conflicts...
    if (m_eventItems.Ready())
        m_eventItems.Reset();   // no items to dequeue

    if (m_rgpItems)
    {
        if (m_pfnItemDtor)
        {
            for (UINT i = 0; i < m_nUsed; i++)
            {
                m_pfnItemDtor(m_rgpItems[i]);
                m_rgpItems[i] = NULL;
            }
        }

        delete[] m_rgpItems;
    }

    m_nUsed = 0;

    DeleteCriticalSection(&m_cs);
}

template <class Type>
BOOL CUnboundedQueue<Type>::Init(DWORD dwInitialSize)
{
    if (m_rgpItems || dwInitialSize < 1)
        return FALSE;

    if (!m_eventItems.Init(TRUE, FALSE, TEXT("CUnboundedQueue_")))
        return FALSE;

    m_rgpItems = new Type*[dwInitialSize];
    if (!m_rgpItems)
        return FALSE;

    m_dwInitialSize = dwInitialSize;
    m_dwCurrentSize = dwInitialSize;

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::GrowQueue()
{
    ASSERT(Ready());

    DWORD dwNewSize = m_dwCurrentSize + m_dwInitialSize;

    Type ** rgpNewBuffer = new Type*[dwNewSize];
    if (!rgpNewBuffer)
        return FALSE;

    memcpy(rgpNewBuffer, m_rgpItems, sizeof(Type*) * m_nUsed);

    delete[] m_rgpItems;

    m_rgpItems = rgpNewBuffer;
    m_dwCurrentSize = dwNewSize;

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::Put(Type * const pItem)
{
    ASSERT(Ready());

    SYNCBLOCK(m_cs);

    ASSERT(m_nUsed <= m_dwCurrentSize);

    if (m_nUsed >= m_dwCurrentSize)
    {
        if (!GrowQueue())
            return FALSE;
    }

    ASSERT(m_nUsed < m_dwCurrentSize);

    // Put the element in our queue.
    m_rgpItems[m_nUsed++] = pItem;

    // There's something new in the queue,
    // so signal waiting threads.
    m_eventItems.Set();

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::Get(Type ** ppItem)
{
    return ConditionalGet(NULL, 0, ppItem);
}

template <class Type>
BOOL CUnboundedQueue<Type>::Peek(Type ** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        *ppItem = m_rgpItems[0];

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type>
HRESULT CUnboundedQueue<Type>::ConditionalGet(const PFN_QUEUE_TEST pfnTest,
                                              const DWORD dwData,
                                              Type** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return S_FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        Type * pItem = m_rgpItems[0];

        // If there is a condition, make sure
        // it is satisfied.
        if (pfnTest && !pfnTest(pItem, dwData))
            return E_FAIL;

        // One less in queue now...
        m_nUsed--;

        if (m_nUsed)
        {
            // Slide remaining items over to the beginning of the buffer.
            memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);
        }
        else
        {
            // The last item was copied from the queue,
            // so reset this event.
            m_eventItems.Reset();
        }

        // NULL out the space that was freed up.
        m_rgpItems[m_nUsed] = NULL;

        // Set the output parameter.
        *ppItem = pItem;

        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

// Generic queue
template <class Type, UINT Size>
class CQueue
{
public:
                CQueue(const BOOL fDontCallDestructors = FALSE);
    virtual     ~CQueue();

    BOOL        Init(const HANDLE hEvent);
    BOOL        Put(Type* const pItem, const DWORD dwTimeout);
    HRESULT     Get(Type*& rpItem, const DWORD dwTimeout);
    BOOL        Peek(Type*& rpItem);
    HRESULT     ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type*& rpItem,
                               const DWORD dwTimeout);
    HRESULT     WaitForNextItem(const DWORD dwTimeout);
    HANDLE      GetPutEvent() { return m_hPutEvent; }
    void        Enum(const PFN_QUEUE_ENUM pfnEnum, const DWORD dwData, const BOOL fClear);
    BOOL        FEmpty();
    BOOL        SetReadLock(BOOL fLock);

protected:
    HRESULT     GetInternal(Type*& rpItem, const DWORD dwTimeout);
    BOOL        PeekInternal(Type*& rpItem);
    BOOL        WaitForEmptySpaceInternal(const DWORD dwTimeout);
    HRESULT     WaitForNextItemInternal(const DWORD dwTimeout);

    Type*               m_rgpItems[Size];
    UINT                m_nUsed;
    HANDLE              m_hGetLockEvent;
    HANDLE              m_hGetEvent;
    HANDLE              m_hPutEvent;
    HANDLE              m_hCancelEvent;
    CRITICAL_SECTION    m_cs;
    BOOL                m_fDontCallDestructors;
    BOOL                m_fInited;
};


// Priority queue
template <class Type, UINT Size>
class CPriorityQueue : public CQueue<Type, Size>
{
public:
                 CPriorityQueue();
                 ~CPriorityQueue();

    BOOL         Put(Type* const pItem, const DWORD dwTimeout);
    HRESULT      Get(Type*& rpItem, const DWORD dwTimeout);
    BOOL         SetPriorityLock(const DWORD dwPriority = MAXDWORD);

protected:
    static BOOL  CheckPriority(void* pItem, DWORD dwInstance) { return (((Type*)pItem)->GetPriority() <= ((CPriorityQueue<Type, Size>*)dwInstance)->m_dwPriorityLock); };

    DWORD        m_dwPriorityLock;
};


// Function to be passed to CDblList::Enum().
//    This function should return TRUE for enumeration to stop.
typedef BOOL (*PFN_LIST_ENUM)(void* pItem, DWORD dwData);


// Generic doubly-linked list
template <class Type>
class CDblList
{
public:
            CDblList();
            ~CDblList();

    BOOL    Add(Type* const pAdd);
    BOOL    Remove(const Type* const pRemove);
    void    Enum(const PFN_LIST_ENUM pfnEnum, const DWORD dwData);
    DWORD   GetElemsNum() {return m_dwElemNums;}

private:
    DWORD               m_dwElemNums; 
    Type*               m_pElems;
    CRITICAL_SECTION    m_cs;
};



//
// Template definitions
//

//
// Queue ctor
//
template <class Type, UINT Size>
CQueue<Type, Size>::CQueue(const BOOL fDontCallDestructors /* = FALSE */)
:  m_nUsed(0),
   m_hGetLockEvent(NULL),
   m_hGetEvent(NULL),
   m_hPutEvent(NULL),
   m_hCancelEvent(NULL),
   m_fDontCallDestructors(fDontCallDestructors),
   m_fInited(FALSE)
{
    // Initialize the critical section
    InitializeCriticalSection(&m_cs);

    for (UINT i = 0; i < Size; i++) {
        m_rgpItems[i] = NULL;
    }
}


//
// Queue dtor
//
template <class Type, UINT Size>
CQueue<Type, Size>::~CQueue()
{
    // Get rid of the events
    if (m_hPutEvent) {
        (void)CloseHandle(m_hPutEvent);
    }

    if (m_hGetEvent) {
        (void)CloseHandle(m_hGetEvent);
    }

    if (m_hGetLockEvent) {
        (void)CloseHandle(m_hGetLockEvent);
    }

    // Delete the elements still in the queue
    for (UINT i = 0; i < m_nUsed; i++) {
        if (m_fDontCallDestructors) {
            // NOTE: We use this to avoid useless asserts when this destructor (called from RIL driver) frees
            //       memory allocated by RIL proxy
            LocalFree(m_rgpItems[i]);
        } else {
            delete m_rgpItems[i];
        }
    }

    // Get rid of the critical section
    DeleteCriticalSection(&m_cs);
}


//
// Initalize the queue
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Init(const HANDLE hEvent)
{
    TBD_ASSERT(hEvent != NULL);

    // If the queue is already initialized, skip this
    if (m_fInited) {
        goto Success;
    }

    // Create the events
    m_hGetLockEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hGetLockEvent) {
        goto Error;
    }

    m_hGetEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hGetEvent) {
        goto Error;
    }

    m_hPutEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hPutEvent) {
        goto Error;
    }

    m_hCancelEvent = hEvent;
    m_fInited = TRUE;

Success:
    return TRUE;

Error:
    if (m_hGetEvent) {
        (void)CloseHandle(m_hGetEvent);
    }
    return FALSE;
}


//
// Enqueue an element
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Put(Type* const pNew, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    TBD_ASSERT(FALSE != m_fInited);
    TBD_ASSERT(pNew != NULL);

    BOOL fRet = FALSE;

    if (m_fInited) {
        if (!WaitForEmptySpaceInternal(dwTimeout)) {
            goto Error;
        }
        TBD_ASSERT(m_nUsed < Size);

        // We have space in the queue
        m_rgpItems[m_nUsed] = pNew;
        m_nUsed++;

        // Signal the Put event
        (void)SetEvent(m_hPutEvent);
    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::Get(Type*& rpItem, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);
    return GetInternal(rpItem, dwTimeout);
}


//
// Dequeue an element (internal version)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::GetInternal(Type*& rpItem, const DWORD dwTimeout)
{
    TBD_ASSERT(FALSE != m_fInited);

    HRESULT hr = S_OK;

    // Initialize the returned pointer in case we fail
    rpItem = NULL;

    if (m_fInited) {
        hr = WaitForNextItemInternal(dwTimeout);
        if (FAILED(hr)) {
            goto Error;
        }

        // Got an item in the queue
        rpItem = m_rgpItems[0];
        m_nUsed--;
        memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);
        m_rgpItems[m_nUsed] = NULL;

        // Signal the Get event
        (void)SetEvent(m_hGetEvent);
    }

Error:
    return hr;
}


//
// Retrieve a pointer to the first element in the queue
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Peek(Type*& rpItem)
{
    SYNCBLOCK(m_cs);
    return PeekInternal(rpItem);
}


//
// Retrieve a pointer to the first element in the queue (internal version)
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::PeekInternal(Type*& rpItem)
{
    TBD_ASSERT(FALSE != m_fInited);

    BOOL fRet = FALSE;

    rpItem = NULL;

    if (!m_nUsed) {
        goto Error;
    }

    rpItem = m_rgpItems[0];
    TBD_ASSERT(NULL != rpItem);
    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element from the queue, if it satisfies a condition
//    (will wait for the next element for a specified timeout)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type*& rpItem,
                                           const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    HRESULT hr = S_OK;

    // Wait for an element to appear in the queue
    hr = WaitForNextItemInternal(dwTimeout);
    if (FAILED(hr)) {
        goto Error;
    }

    // Peek the first element
    if (!PeekInternal(rpItem)) {
        TBD_ASSERT(FALSE);
    }

    // See if the first element satisfies the condition
    if (!pfnTest(rpItem, dwData)) {
        rpItem = NULL;
        hr = E_FAIL;
        goto Error;
    }

    // Retrieve the first element
    if (FAILED(GetInternal(rpItem, INFINITE))) {
        TBD_ASSERT(FALSE);
    }
    TBD_ASSERT(NULL != rpItem);

Error:
    return hr;
}


//
// Wait until an element appears in the queue
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::WaitForNextItem(const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);
    return WaitForNextItemInternal(dwTimeout);
}


//
// Wait until an item appears in the queue (internal version)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::WaitForNextItemInternal(const DWORD dwTimeout)
{
    //TBD_FUNCTION(CQueue::WaitForNextItemInternal);
    TBD_ASSERT(FALSE != m_fInited);

    HANDLE rghEvents[3] = { m_hPutEvent, m_hCancelEvent, m_hGetLockEvent };
    DWORD dwWait;
    HRESULT hr = S_OK;

    if (m_fInited) {
        while(1) {
            // Are there any items in the queue?
            if (m_nUsed) {
                // Yes -- proceed
                break;
            } else {
                // No - need to wait for Put to happen
                UNSYNCBLOCK(m_cs);

                dwWait = WaitForMultipleObjects(3, rghEvents, FALSE, dwTimeout);
                if (WAIT_OBJECT_0 + 2 == dwWait) {
                    // We hit the terminate event -> quit
                    hr = RIL_E_RADIOOFF;
                    goto Error;
                } else if (WAIT_OBJECT_0 + 1 == dwWait) {
                    // We hit the terminate event -> quit
                    hr = RIL_E_CANCELLED;
                    goto Error;
                } else if (WAIT_TIMEOUT == dwWait) {
                    // We timed out -> quit
                    hr = RIL_E_TIMEDOUT;
                    goto Error;
                } else {
                    TBD_ASSERT(WAIT_OBJECT_0 == dwWait);
                }
            }
        }

        TBD_ASSERT(m_nUsed != 0);
    }

Error:
    return hr;
}


//
// Wait until an empty slot appears in the queue (internal version)
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::WaitForEmptySpaceInternal(const DWORD dwTimeout)
{
    TBD_ASSERT(FALSE != m_fInited);

    HANDLE rghEvents[2] = { m_hGetEvent, m_hCancelEvent };
    DWORD dwWait;
    BOOL fRet = FALSE;

    if (m_fInited) {
        while (1) {
            // Is there space in the queue?
            if (m_nUsed < Size) {
                // Yes -- proceed
                break;
            } else {
                TBD_ASSERT(Size == m_nUsed);

                // No -- need to wait for Get to happen
                UNSYNCBLOCK(m_cs);

                dwWait = WaitForMultipleObjects(2, rghEvents, FALSE, dwTimeout);
                if (WAIT_OBJECT_0 != dwWait) {
                    // We hit the terminate event or timed out
                    goto Error;
                }
            }
        }
    }

    TBD_ASSERT(m_nUsed < Size);
    fRet = TRUE;

Error:
    return fRet;
}


//
// Enum all queue elements, calling the provided routine for each item
//
template <class Type, UINT Size>
void CQueue<Type, Size>::Enum(const PFN_QUEUE_ENUM pfnEnum, const DWORD dwData, const BOOL fClear)
{
    SYNCBLOCK(m_cs);

    TBD_ASSERT(FALSE != m_fInited);

    if (m_fInited) {
        for (UINT i = 0; i < m_nUsed; i++) {
            if (pfnEnum((void*)m_rgpItems[i], dwData)) {
                break;
            }
        }

        if (fClear) {
            // We also need to clear the queue
            for (i = 0; i < m_nUsed; i++) {
                delete m_rgpItems[i];
                m_rgpItems[i] = NULL;
            }
            m_nUsed = 0;
        }
    }
}


//
// Lock or unlock the Get() function
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::SetReadLock(BOOL fLock)
{
    SYNCBLOCK(m_cs);
    return fLock ? ResetEvent(m_hGetLockEvent) : SetEvent(m_hGetLockEvent);
}


//
// Determine if the queue is empty
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::FEmpty()
{
    SYNCBLOCK(m_cs);
    return !m_nUsed;
}


//
// Priority queue ctor
//
template <class Type, UINT Size>
CPriorityQueue<Type, Size>::CPriorityQueue()
: CQueue<Type, Size>(FALSE)
{
    m_dwPriorityLock = MAXDWORD;
}


//
// Priority queue dtor
//
template <class Type, UINT Size>
CPriorityQueue<Type, Size>::~CPriorityQueue()
{
}


//
// Enqueue an element
//
template <class Type, UINT Size>
BOOL CPriorityQueue<Type, Size>::Put(Type* const pNew, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    TBD_ASSERT(FALSE != m_fInited);
    TBD_ASSERT(pNew != NULL);

    UINT i;
    BOOL fRet = FALSE;

    if (m_fInited) {

        if (!WaitForEmptySpaceInternal(dwTimeout)) {
            goto Error;
        }
        TBD_ASSERT(m_nUsed < Size);

        // We have space in the queue -- find the correct spot for this item
        for (i = 0; i < m_nUsed; i++) {
            if (pNew->GetPriority() < m_rgpItems[i]->GetPriority()) {
                // We found the first item whose pri is lower (i.e., greater) than the one for the new item --
                //     shift other items in the queue
                memmove(&m_rgpItems[i + 1], &m_rgpItems[i], sizeof(Type*) * (m_nUsed - i));
                break;
            }
        }

        // Insert the new item
        m_rgpItems[i] = pNew;
        m_nUsed++;
        
        RILRetailTrace((TEXT("CPriorityQueue m_nUsed = %d\n"), m_nUsed));
        RILRetailTrace((TEXT("CPriorityQueue *****************************************\n")));
        // Signal the Put event
        (void)SetEvent(m_hPutEvent);

    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element
//
template <class Type, UINT Size>
HRESULT CPriorityQueue<Type, Size>::Get(Type*& rpItem, const DWORD dwTimeout)
{
    HRESULT hr;
    while ((hr = ConditionalGet(CheckPriority, (DWORD)this, rpItem, dwTimeout)) == E_FAIL) {
        // Wait for next put event
        HANDLE rghEvents[3] = { m_hPutEvent, m_hCancelEvent, m_hGetLockEvent };
        switch (WaitForMultipleObjects(3, rghEvents, FALSE, dwTimeout)) {
            case (WAIT_OBJECT_0 + 2):
                // We hit the terminate event -> quit
                return RIL_E_RADIOOFF;
            case (WAIT_OBJECT_0 + 1):
                // We hit the terminate event -> quit
                return RIL_E_CANCELLED;
            case WAIT_TIMEOUT:
                // We timed out -> quit
                return RIL_E_TIMEDOUT;
        }
    }
    return hr;
}


//
// Set the priority lock (block all elements with lower priority)
//
template <class Type, UINT Size>
BOOL CPriorityQueue<Type, Size>::SetPriorityLock(const DWORD dwPriority)
{
    SYNCBLOCK(m_cs);
    m_dwPriorityLock = dwPriority;
    // Signal the Put event
    if (!FEmpty()) {
        (void)SetEvent(m_hPutEvent);
    }
    return TRUE;
}


//
// List ctor
//
template <class Type>
CDblList<Type>::CDblList()
: m_pElems(NULL), m_dwElemNums(0)
{
    InitializeCriticalSection(&m_cs);
}


//
// List dtor
//
template <class Type>
CDblList<Type>::~CDblList()
{
    DeleteCriticalSection(&m_cs);
}


//
// Add an item to the list
//
template <class Type>
BOOL CDblList<Type>::Add(Type* const pAdd)
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    // Check thaat the new element exists
    if (!pAdd) {
        goto Error;
    }

    // Add the new element at the front
    pAdd->SetNext(m_pElems);
    pAdd->SetPrev(NULL);

    if (pAdd->GetNext()) {
        pAdd->GetNext()->SetPrev(pAdd);
    }

    m_pElems = pAdd;
    m_dwElemNums++;
    fRet = TRUE;

Error:
    return fRet;
}


//
// Remove an item from the list
//
template <class Type>
BOOL CDblList<Type>::Remove(const Type* const pRemove)
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    // Check that the element to be removed exists
    if (!pRemove) {
        goto Error;
    }

    // Is this element head of the list?
    if (pRemove == m_pElems) {
        // Yes
        m_pElems = (Type*)pRemove->GetNext();
    } else {
        // No
        if (pRemove->GetPrev())
            pRemove->GetPrev()->SetNext(pRemove->GetNext());
    }

    // Re-route the links
    if (pRemove->GetNext()) {
        pRemove->GetNext()->SetPrev(pRemove->GetPrev());
    }
    
    m_dwElemNums--;
    fRet = TRUE;

Error:
    return fRet;
}


//
// Enum all list elements, calling the provided routine for each item
//
template <class Type>
void CDblList<Type>::Enum(const PFN_LIST_ENUM pfnEnum, const DWORD dwData)
{
    SYNCBLOCK(m_cs);

    Type* pNext;
    for (Type* pWalk = m_pElems; pWalk; pWalk = pNext) {
        pNext = (Type*)pWalk->GetNext();
        if (pfnEnum((void*)pWalk, dwData)) {
            break;
        }
    }
}


